import asyncio
import time
from setting import DEBUG_PRINT
from until import dynamic_route, move_window_by_title
from proxy import XiachufangIPPool
from userAgent import XiachufangUserAgentPool
from taskLog import AsyncTaskLogger

class ContextPagePool:
    def __init__(self, browser, pool_size, IPTimeOout):
        self.browser = browser
        self.pool_size = pool_size
        self.IPTimeOout = IPTimeOout
        self.proxyPool = XiachufangIPPool(IPTimeOout)
        self.userAgentPool = XiachufangUserAgentPool()
        self.pool = []
        self.lock = asyncio.Lock()

    async def init_pool(self):
        for index in range(self.pool_size):
            context, page, ip_info = await self._create_context_page(change_ip=True)
            self.pool.append({
                "context": context,
                "page": page,
                "pageIndex": index,
                "ip_info": ip_info,
                "in_use": False,
                "last_used": time.time(),
                # "other_page_titles": []
            })

    async def _create_context_page(self, change_ip):
        proxy_info = self.proxyPool.geneProxyOption(change_ip)
        user_agent = self.userAgentPool.getUserAgent(change_ip)
        if proxy_info["proxy_options"]:
            context = await self.browser.new_context(
                user_agent=user_agent,
                viewport={"width": 1500, "height": 300},
                proxy=proxy_info["proxy_options"]
            )
        else:
            context = await self.browser.new_context(
                user_agent=user_agent,
                viewport={"width": 1500, "height": 300},
            )
        page = await context.new_page()
        await page.route("**/*", dynamic_route)
        return context, page, proxy_info

    async def acquire(self, task_id, change_ip=False):
        if DEBUG_PRINT >= 1:
            logger = AsyncTaskLogger.get_logger(task_id)
            logger.info(f"acquire started...")

        t1_acquire = time.time()
        if DEBUG_PRINT >=2:
            t2_acquire = time.time()
            msg = f'task_id={task_id} 1--- acquire time: {round(t2_acquire-t1_acquire, 3)}'
            logger.debug(f"{msg}")
        
        async with self.lock:
            # 1. 找到一个未被占用的池元素
            available_items = [item for item in self.pool if not item["in_use"]]
            if not available_items:
                raise RuntimeError("No available context/page in pool!")
                # 理论上不会走到这里

            selected_item = min(available_items, key=lambda x: x["last_used"])


            isHealth = False
            try:
                await asyncio.wait_for(selected_item["page"].title(), timeout=3)
                isHealth = True
            except Exception as e:
                isHealth = False

            # 2. 判断是否需要更换ip/context
            ip_expired = (time.time() - selected_item["ip_info"]["c_time"]) > (self.IPTimeOout-20)  # 3分钟-20秒
            if change_ip or ip_expired or (not isHealth):
                # 销毁旧的
                if DEBUG_PRINT >=2:
                    msg = f'task_id={task_id} 1--- 销毁旧的 page'
                    logger.debug(f"{msg}")
                await selected_item["page"].unroute("**/*")
                await selected_item["page"].close()
                await selected_item["context"].close()
                context, page, ip_info = await self._create_context_page(change_ip=True)
                selected_item["context"] = context
                selected_item["page"] = page
                selected_item["ip_info"] = ip_info

            selected_item["in_use"] = True
            selected_item["last_used"] = time.time()
            pIp = selected_item['ip_info']['proxy_options']['server'].replace('http://', 'ip = ') if selected_item['ip_info']['proxy_options'] else 'ip = '
            if DEBUG_PRINT >=2:
                t3_acquire = time.time()
                msg = f'task_id={task_id} 3--- ip = {pIp} time: {round(t3_acquire-t1_acquire, 3)}'
                logger.debug(f"{msg}")
            if DEBUG_PRINT >= 1:
                logger.info(f"task_id={task_id} acquire Completed")
            return selected_item


    async def release(self, item):
        async with self.lock:
            item["in_use"] = False
            item["last_used"] = time.time()

    async def close(self):
        for item in self.pool:
            await item["page"].unroute("**/*")
            await item["page"].close()
            await item["context"].close()